package org.jeeasy.common.core.controller;

import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeeasy.common.core.domain.vo.R;
import org.jeeasy.common.core.enums.RestCode;
import org.jeeasy.common.core.exception.JeeasyException;
import org.springframework.core.MethodParameter;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.List;
import java.util.stream.Collectors;

import static org.jeeasy.common.core.enums.RestCode.BAD_REQUEST;

/**
 * @author AlpsDDJ
 * @date 2020/11/11
 */
@Slf4j
@RestControllerAdvice
public class JeeasyControllerAdvice implements ResponseBodyAdvice<Object> {

    @ExceptionHandler(Exception.class)
    public R<?> handleException(Exception e, HttpServletResponse response) {
        String header = response.getHeader("Content-Type");
        if (StringUtils.isNoneEmpty(header) && header.contains("text/event-stream")) {
            return null;
        }
        log.error(e.getMessage(), e);
        return R.error("操作失败，" + e.getMessage());
    }

    @ExceptionHandler(JeeasyException.class)
    public R<?> jeeasyExceptionHandler(JeeasyException ex) {
        log.error(ex.getMessage());
        return R.error(ex.getCode(), ex.getMessage());
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public R<?> handlerNoFoundException(NoHandlerFoundException e) {
        log.error(e.getMessage(), e);
        throw new JeeasyException(RestCode.NOT_FOUND);
//        return R.notFound("路径不存在，请检查路径是否正确");
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public R<?> handleDuplicateKeyException(DuplicateKeyException e) {
        log.error(e.getMessage(), e);
        return R.error("数据库中已存在该记录");
    }

//    @ExceptionHandler({UnauthorizedException.class, AuthorizationException.class})
//    public R<?> handleAuthorizationException(AuthorizationException e){
//        log.error(e.getMessage(), e);
//        return Result.noauth("没有权限，请联系管理员授权");
//    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R<?> httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        StringBuilder sb = new StringBuilder();
        sb.append("不支持");
        sb.append(e.getMethod());
        sb.append("请求方法，");
        sb.append("支持以下");
        String[] methods = e.getSupportedMethods();
        if (methods != null) {
            for (String str : methods) {
                sb.append(str);
                sb.append("、");
            }
        }
        log.error(sb.toString(), e);
        throw new JeeasyException(RestCode.NOT_SUPPORTED, sb.toString());
//        return R.notSupported(sb.toString());
    }

    /**
     * spring默认上传大小100MB 超出大小捕获异常MaxUploadSizeExceededException
     */
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public R<?> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
        log.error(e.getMessage(), e);
        return R.error("文件大小超出10MB限制, 请压缩或降低文件质量! ");
    }

    @ExceptionHandler(DataIntegrityViolationException.class)
    public R<?> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
        log.error(e.getMessage(), e);
        return R.error("字段太长,超出数据库字段的长度");
    }

    @Override
    public boolean supports(@NonNull MethodParameter methodParameter, @NonNull Class<? extends HttpMessageConverter<?>> aClass) {
        if (log.isDebugEnabled()) {
            log.debug("MethodParameter:{}", methodParameter);
            log.debug("converterType:{}", aClass);
        }
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object o, @NonNull MethodParameter methodParameter, @NonNull MediaType mediaType, @NonNull Class aClass, ServerHttpRequest request, @NonNull ServerHttpResponse response) {
        if (log.isDebugEnabled()) {
            log.debug(request.getURI().getPath());
        }
        if (o instanceof R<?> result) {
            if (!result.isSuccess()) {
                try {
                    HttpStatus httpStatus = HttpStatus.valueOf(result.getCode());
                    response.setStatusCode(httpStatus);
                    return result;
                } catch (Exception e) {
//                    e.printStackTrace();
                }
            } else {
                HttpMethod method = request.getMethod();
                if (HttpMethod.DELETE.equals(method)) {
                    result.setMessage("删除成功");
                }
                if (HttpMethod.PUT.equals(method)) {
                    result.setMessage("修改成功");
                }
            }
        }
        return o;
    }


    /**
     * 处理 SpringMVC 请求参数缺失
     * <p>
     * 例如说，接口上设置了 @RequestParam("xx") 参数，结果并未传递 xx 参数
     */
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public R<?> missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException ex) {
        log.warn("[missingServletRequestParameterExceptionHandler]", ex);
        return R.error(BAD_REQUEST.getCode(), String.format("请求参数缺失:%s", ex.getParameterName()));
    }

    /**
     * 处理 SpringMVC 请求参数类型错误
     * <p>
     * 例如说，接口上设置了 @RequestParam("xx") 参数为 Integer，结果传递 xx 参数类型为 String
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public R<?> methodArgumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchException ex) {
        log.warn("[missingServletRequestParameterExceptionHandler]", ex);
        return R.error(BAD_REQUEST.getCode(), String.format("请求参数类型错误:%s", ex.getMessage()));
    }

    /**
     * 处理 SpringMVC 参数校验不正确
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R<?> methodArgumentNotValidExceptionExceptionHandler(MethodArgumentNotValidException ex) {
        log.warn("[methodArgumentNotValidExceptionExceptionHandler]", ex);
        BindingResult bindingResult = ex.getBindingResult();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        String errorMsg = fieldErrors.stream().map(fieldError -> String.format("%s:%s;", fieldError.getField(), fieldError.getDefaultMessage())).collect(Collectors.joining(""));
        return R.error(BAD_REQUEST.getCode(), errorMsg);
    }

    /**
     * 处理 SpringMVC 参数绑定不正确，本质上也是通过 Validator 校验
     */
    @ExceptionHandler(BindException.class)
    public R<?> bindExceptionHandler(BindException ex) {
        log.warn("[handleBindException]", ex);
        FieldError fieldError = ex.getFieldError();
        assert fieldError != null; // 断言，避免告警
        return R.error(BAD_REQUEST.getCode(), String.format("请求参数不正确:%s", fieldError.getDefaultMessage()));
    }

    /**
     * 处理 Validator 校验不通过产生的异常
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    public R<?> constraintViolationExceptionHandler(ConstraintViolationException ex) {
        log.warn("[constraintViolationExceptionHandler]", ex);
        ConstraintViolation<?> constraintViolation = ex.getConstraintViolations().iterator().next();
        return R.error(BAD_REQUEST.getCode(), String.format("请求参数不正确:%s", constraintViolation.getMessage()));
    }

}
